package handler

import (
	"encoding/json"
	"fmt"
	"gitee.com/DonHz/cloud-file-storage/common"
	"gitee.com/DonHz/cloud-file-storage/config"
	"gitee.com/DonHz/cloud-file-storage/db"
	"gitee.com/DonHz/cloud-file-storage/meta"
	"gitee.com/DonHz/cloud-file-storage/mq"
	"gitee.com/DonHz/cloud-file-storage/store/oss"
	"gitee.com/DonHz/cloud-file-storage/util"
	"github.com/gin-gonic/gin"
	"io"
	"net/http"
	"os"
	"strconv"
	"time"
)

func UploadHandler(c *gin.Context) {
	// 接收文件流，并存储到本地目录
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		fmt.Println("Failed to get data, error：", err)
		return
	}
	defer file.Close()

	// 创建文件的元信息
	fileMeta := meta.FileMeta{
		FileName: header.Filename,
		Location: "./tmp/" + header.Filename,
		UploadAt: time.Now().Format("2006-01-02 15:04:05"),
	}

	// 创建一个本地文件来接收文件流
	newFile, err := os.Create(fileMeta.Location)
	if err != nil {
		fmt.Println("Failed to create file, error：", err)
		return
	}
	defer newFile.Close()

	// 将接收的文件流复制到创建的本地文件中
	fileMeta.FileSize, err = io.Copy(newFile, file)
	if err != nil {
		fmt.Println("Failed to save data into file, error：", err)
		return
	}

	// 计算文件的Sha1值，并更新记录的总文件元信息
	newFile.Seek(0, 0)
	fileMeta.FileSha1 = util.FileSha1(newFile)

	newFile.Seek(0, 0)
	ossPath := "oss/" + fileMeta.FileSha1
	//err = oss.Bucket().PutObject(ossPath, newFile)
	//if err != nil {
	//	fmt.Println(err)
	//	c.JSON(http.StatusOK, gin.H{
	//		"msg":      "上传失败",
	//		"fileName": header.Filename,
	//	})
	//	return
	//}
	data := mq.TransferData{
		FileHash:      fileMeta.FileSha1,
		CurLocation:   fileMeta.Location,
		DestLocation:  ossPath,
		DestStoreType: common.StoreOSS,
	}
	pubData, err := json.Marshal(data)
	if err != nil {
		fmt.Println("Failed to marshal data, error：", err)
		return
	}
	IsSucess := mq.Publish(config.TransExchangeName, config.TransOSSRoutingKey, pubData)
	if !IsSucess {
		// TODO:重试发送消息逻辑
		fmt.Println("Failed to publish message to mq")
	}
	fileMeta.Location = ossPath

	ok := meta.UpdateFileMetaDB(fileMeta)
	if !ok {
		fmt.Println("Failed to update file meta to mysql")
		return
	}
	fmt.Println(fileMeta)
	// TODO: 更新用户文件表记录
	username := c.Query("username")
	ok = db.OnUserFileUploadFinished(username, fileMeta.FileSha1, fileMeta.FileName, fileMeta.FileSize)
	if !ok {
		fmt.Println("Failed to update user file table")
		return
	}
	// 返回响应
	c.JSON(http.StatusOK, gin.H{
		"msg":      "上传成功",
		"fileName": header.Filename,
	})
	c.Redirect(http.StatusFound, "/static/view/home.html")
}

// 获取文件元信息
func GetFileMetaHandler(c *gin.Context) {
	// 解析客户端参数
	//filehash := c.Query("filehash")
	//fMeta, err := meta.GetFileMetaDB(filehash)
	username := c.Query("username")
	limitStr := c.Query("limit")
	limit, _ := strconv.Atoi(limitStr)
	fileMetas, err := db.QueryUserFileMetas(username, limit)
	if err != nil {
		fmt.Println("Failed to get file meta from mysql, error：", err)
		return
	}
	c.JSON(http.StatusOK, fileMetas)
}

// 更新文件元信息（重命名）
func FileMetaUpdateHandler(c *gin.Context) {
	// 解析客户端参数
	opType, ok := c.GetPostForm("op")
	if !ok {
		fmt.Println("Failed to get opType")
		return
	}
	fileSha1, ok := c.GetPostForm("filehash")
	if !ok {
		fmt.Println("Failed to get fileSha1")
		return
	}
	newFileName, ok := c.GetPostForm("filename")
	if !ok {
		fmt.Println("Failed to get file_name")
		return
	}

	if opType != "0" {
		c.JSON(http.StatusForbidden, "参数错误")
		return
	}

	// 重命名
	fMeta, err := meta.GetFileMetaDB(fileSha1)
	if err != nil {
		fmt.Println("Failed to get file meta from mysql, error：", err)
		return
	}
	fMeta.FileName = newFileName
	fMeta.UpdateAt = time.Now().Format("2006-01-02 15:04:05")
	ok = meta.UpdateFileMetaDB(fMeta)
	if !ok {
		fmt.Println("Failed to update file meta to mysql")
	}

	c.JSON(http.StatusOK, fMeta)
}

// 下载文件
func DownloadHandler(c *gin.Context) {
	// 获取下载文件的元信息
	filehash := c.Query("filehash")
	fMeta, err := meta.GetFileMetaDB(filehash)
	if err != nil {
		fmt.Println("Failed to get file meta from mysql, error：", err)
		return
	}
	// 从文件元信息中获取文件的保存位置
	f, err := os.Open(fMeta.Location)
	if err != nil {
		fmt.Println("Failed to open file from cloud, error：", err)
		return
	}
	defer f.Close()
	// 这里可以优化，这里时小文件，如果是大文件，一节一节读取
	data, err := io.ReadAll(f)
	if err != nil {
		fmt.Println("Failed to read file, error：", err)
		return
	}
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+fMeta.FileName)
	c.Header("Content-Transfer-Encoding", "binary")
	c.Header("Cache-Control", "no-cache")
	c.Writer.Write(data)
	c.JSON(http.StatusOK, data)
}

func FileDeleteHandler(c *gin.Context) {
	// 删除索引数据
	fileSha1, ok := c.GetPostForm("filehash")
	if !ok {
		fmt.Println("Failed to get filehash")
		return
	}
	meta.RemoveFileMeta(fileSha1)
	// 删除系统保存的文件
	// TODO 这里有问题，重命名之后的本地存储名字没改，导致删除失败
	fMeta, err := meta.GetFileMetaDB(fileSha1)
	if err != nil {
		fmt.Println("Failed to get file meta from mysql, error：", err)
		return
	}
	if err := os.Remove(fMeta.Location); err != nil {
		fmt.Println("删除失败，filename：", fMeta.FileName)
	}

	c.JSON(http.StatusOK, gin.H{
		"msg": "success",
	})
}

// 秒传接口
func TryFastUploadHandler(c *gin.Context) {
	// 解析请求参数
	username := c.Query("username")
	filehash := c.Query("filehash")
	filename := c.Query("filename")
	filesizeStr := c.Query("filesize")
	filesize, _ := strconv.ParseInt(filesizeStr, 10, 64)
	// 从文件表中查询相同hash的文件记录
	fileMeta, err := meta.GetFileMetaDB(filehash)
	if err != nil {
		fmt.Println("Failed to get file meta from mysql, error：", err)
		resp := util.RespMsg{
			Code: -1,
			Msg:  "秒传失败，请访问普通上传接口",
		}
		c.JSON(http.StatusOK, resp)
		return
	}
	// 查不到记录则返回秒传失败
	if fileMeta == (meta.FileMeta{}) {
		resp := util.RespMsg{
			Code: -1,
			Msg:  "秒传失败，请访问普通上传接口",
		}
		c.JSON(http.StatusOK, resp)
		return
	}
	// 上传过则将文件信息写入用户文件表，返回成功
	ok := db.OnUserFileUploadFinished(username, filehash, filename, filesize)
	if !ok {
		fmt.Println("秒传失败")
		return
	}
	resp := util.RespMsg{
		Code: 0,
		Msg:  "秒传成功",
	}
	c.JSON(http.StatusOK, resp)
}

func DownloadURLHandler(c *gin.Context) {
	c.Request.ParseForm()
	filehash := c.Request.Form.Get("filehash")
	// 从文件表查找记录
	filehash1 := c.Query("filehash")
	fmt.Println(filehash1)
	row, _ := db.GetFileMeta(filehash)
	signedURL := oss.DownloadURL(row.FileAddr.String)
	c.Writer.Write([]byte(signedURL))
	//c.JSON(http.StatusOK, gin.H{
	//	"url": signedURL,
	//})
}
